package com.csx.framework.springboot.sample.aop.aspect;

import com.csx.framework.springboot.sample.aop.annotation.RepeatSubmit;
import com.csx.framework.springboot.sample.aop.controller.RepeatSubmitController;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

/**
 * 定义一个切面类
 **/
@Aspect
@Component
public class RepeatSubmitAspect {
    private final Logger logger = LoggerFactory.getLogger(RepeatSubmitAspect.class);

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 定义 @Pointcut注解表达式, 通过特定的规则来筛选连接点, 就是Pointcut，选中那几个你想要的方法
     * 在程序中主要体现为书写切入点表达式（通过通配、正则表达式）过滤出特定的一组 JointPoint连接点
     * 方式一：@annotation：当执行的方法上拥有指定的注解时生效
     * 方式二：execution：一般用于指定方法的执行
     */
    @Pointcut("@annotation(repeatSubmit)")
    public void pointCutNoRepeatSubmit(RepeatSubmit repeatSubmit) {}

    /**
     * 环绕通知
     */
    @Around(value = "pointCutNoRepeatSubmit(repeatSubmit)", argNames = "joinPoint,repeatSubmit")
    public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        long accountNo = 123456;
        //用于记录成功或者失败
        boolean res = false;
        //防重提交类型
        String type = repeatSubmit.limitType().name();
        if (type.equalsIgnoreCase(RepeatSubmit.Type.PARAM.name())) {
            //方式一，参数形式防重提交
        }
        else {
            //方式二，令牌形式防重提交
            String requestToken = request.getHeader("request-token");
            if (StringUtils.isBlank(requestToken)) {
                throw new RuntimeException("业务异常,请求 token为空");
            }
            String key = String.format("submit", accountNo, requestToken);
            /**
             * 提交表单的token key
             * 只有删除成功,表示该请求是第一次合法请求
             */
            res = Boolean.TRUE.equals(redisTemplate.delete(key));
        }
        if (!res) {
            logger.error("请求重复提交");
            // 实际项目开发中可以抛出业务异常,然后对业务异常做特殊处理,返回异常提示信息
            throw new RuntimeException("请求重复提交");
        }
        logger.info("环绕通知执行前");
        Object obj = joinPoint.proceed();
        logger.info("环绕通知执行后");
        return obj;
    }


}
